package com.xinsoft.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.Pair;
import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.collect.Lists;
import com.xinsoft.common.Response;
import com.xinsoft.common.exception.BDException;
import com.xinsoft.constant.Const;
import com.xinsoft.entity.dto.*;
import com.xinsoft.entity.po.*;
import com.xinsoft.entity.vo.MesDispatchHisVo;
import com.xinsoft.entity.vo.MesDispatchVo;
import com.xinsoft.entity.vo.MesProcessCardVo;
import com.xinsoft.entity.vo.MesProcessReportAddVo;
import com.xinsoft.service.*;
import com.xinsoft.mapper.MesDispatchMapper;
import com.xinsoft.utils.TokenUtil;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

/**
* @author 13370
* @description 针对表【mes_dispatch(工单)】的数据库操作Service实现
* @createDate 2023-07-31 16:05:10
*/
@Service
public class MesDispatchServiceImpl extends ServiceImpl<MesDispatchMapper, MesDispatch>
    implements MesDispatchService{

    @Autowired
    private MesDispatchHisService mesDispatchHisService;
    @Autowired
    private SysUserService sysUserService;
    @Autowired
    private MesProcessReportService mesProcessReportService;
    @Autowired
    private SysMaterialService sysMaterialService;
    @Autowired
    private MesProcessPlanService mesProcessPlanService;
    @Autowired
    private MesProcessPlanDetailService mesProcessPlanDetailService;

    @Autowired
    private MesDocumentService mesDocumentService;

    @Resource
    public SysDocumentNumService sysDocumentNumService;

    @Autowired
    private SysFileHisService sysFileHisService;
    @Autowired
    private MesProducePlanService mesProducePlanService;

    @Override
    public Response<?> findPageListByParam(MesDispatchPageParam mesDispatchPageParam) {
        Page<MesDispatchVo> page = new Page<>(mesDispatchPageParam.getCurrentPage(), mesDispatchPageParam.getPageSize());
        IPage<MesDispatchVo> pageParamList = this.baseMapper.findPageListByParam(page, mesDispatchPageParam);
        // 待制数量逻辑调整 2023-11-2
        List<MesDispatchVo> list = pageParamList.getRecords();
        list.stream().forEach(item -> {
            if (ObjectUtil.isEmpty(item.getPreConformityCount()) || item.getPreConformityCount().stripTrailingZeros().equals(BigDecimal.ZERO)){
                item.setResidueNum(item.getPlanNum());
                return;
            }
            item.setResidueNum(item.getPreConformityCount()
                .subtract(ObjectUtil.isEmpty(item.getCompleteNum()) ? BigDecimal.ZERO : item.getCompleteNum()));
        });
        return Response.succeed(pageParamList);
    }

    @Override
    public Response<?> detail(Integer id) {
        MesDispatchPageParam param = new MesDispatchPageParam();
        param.setId(id);
        List<MesDispatchVo> vos = this.baseMapper.findPageListByParam(param);
        MesDispatchVo mesDispatchVo = vos.get(0);

        mesDispatchVo.setOperatorUserMap(new ArrayList<>());
        mesDispatchVo.setOperatorInitMap(new ArrayList<>());

        ArrayList workingOperators = ObjectUtil.isEmpty(mesDispatchVo.getOperatorUserIds()) ? new ArrayList() : new ArrayList(Arrays.asList(mesDispatchVo.getOperatorUserIds().split(",")));
        JSONArray operatorInits = JSON.parseArray(mesDispatchVo.getOperatorInit());
        ArrayList allOperators = new ArrayList();
        allOperators.addAll(workingOperators);
        allOperators.addAll(operatorInits);
        List<SysUser> userDOS = sysUserService.list(new LambdaQueryWrapper<SysUser>().in(SysUser::getId, allOperators));
        Map<String, String> userDOSMap = userDOS.stream().collect(Collectors.toMap(item -> item.getId().toString(), item -> item.getRealName(), (v1, v2) -> v1));
//        List<HashMap<String, String>> userList
        Object operatorUserMap = workingOperators.stream().map(item -> {
            HashMap<String, String> map = new HashMap<>();
            map.put("name", userDOSMap.get(item));
            map.put("id", item.toString());
            return map;
        }).collect(Collectors.toList());
        Object operatorInitMap = operatorInits.stream().map(item -> {
            HashMap<String, String> map = new HashMap<>();
            map.put("name", userDOSMap.get(item.toString()));
            map.put("id", item.toString());
            return map;
        }).collect(Collectors.toList());

        mesDispatchVo.setOperatorUserMap(operatorUserMap);
        mesDispatchVo.setOperatorInitMap(operatorInitMap);
        return Response.succeed(mesDispatchVo);
    }

    @Override
    public Response<?> workHangUp(MesDispatchHangUpParam mesDispatchPageParam) {
        Integer dispatchId = mesDispatchPageParam.getDispatchId();
        MesDispatch mesDispatch = this.baseMapper.selectById(dispatchId);
        //校验派工单状态
        if (!Const.PlanType.kai_gong.equals(mesDispatch.getDispatchStatus())){
            return Response.fail("该派工单未开工，不能挂起！");
        }

        List<Integer> operators = JSON.parseArray(mesDispatchPageParam.getReportUsers(), Integer.class);
        List<MesDispatchHis> dispatchHisDOS = mesDispatchHisService.list(new LambdaQueryWrapper<MesDispatchHis>()
                .eq(MesDispatchHis::getDispatchId, dispatchId).eq(MesDispatchHis::getSign, 1).isNull(MesDispatchHis::getHangTime));
        List<MesDispatchHis> kaiGongDOS = dispatchHisDOS.stream()
                .filter(item -> operators.contains(item.getStartHangId())).collect(Collectors.toList());
        Date nowTime = new Date();
        //校验操作工
        if (kaiGongDOS.size() != operators.size()){
            List<Integer> operatorNows = kaiGongDOS.stream().map(MesDispatchHis::getStartHangId).collect(Collectors.toList());
            operators.removeAll(operatorNows);
            List<SysUser> userErrorDOS = sysUserService.list(new LambdaQueryWrapper<SysUser>().in(SysUser::getId, operators));
            return Response.fail("未查询到" + StringUtils.join(userErrorDOS.stream().map(SysUser::getRealName).collect(Collectors.toList()), ",") + "的开工记录！");
        }

        //1、查询已有开工记录、将开工停止
        if (ObjectUtil.isNotEmpty(kaiGongDOS)){
            kaiGongDOS.forEach(item -> {
                item.setHangTime(nowTime);
                item.setManHour(dateHour(item.getStartTime(), nowTime));
            });
        }

        //2、生成挂起记录 考虑到以后可能会统计每个操作工的工时，开工挂起记录精确到操作工
        List<MesOperatorDevicesBindDTO> bindDOS = JSON.parseArray(mesDispatch.getOperatorDevicesBind(), MesOperatorDevicesBindDTO.class);
        Map<Integer, MesOperatorDevicesBindDTO> bindDOSMap = bindDOS.stream().collect(Collectors.toMap(item -> item.getOperator(), item -> item, (v1, v2) -> v1));
        List<MesDispatchHis> guaQiDTO = operators.stream().map(item -> {
            MesDispatchHis guaQiHis = new MesDispatchHis(dispatchId, nowTime, item, 0, mesDispatchPageParam.getReason());
            guaQiHis.setCreateBy(TokenUtil.getUserId());
            bindDOS.remove(bindDOSMap.get(item));
            bindDOSMap.remove(item);
            return guaQiHis;
        }).collect(Collectors.toList());
        guaQiDTO.addAll(kaiGongDOS);
        mesDispatchHisService.saveOrUpdateBatch(guaQiDTO);


        if (ObjectUtil.isEmpty(bindDOS)) {
            mesDispatch.setDispatchStatus(Const.PlanType.gua_qi);
        }
        JSONArray parseArray = JSON.parseArray(mesDispatch.getOperator());
        parseArray.removeAll(operators);
        List<Integer> deviceIds = bindDOS.stream().flatMap(item -> item.getDevices().stream()).collect(Collectors.toList());
        List<Integer> operatorIds = bindDOS.stream().map(item -> item.getOperator()).collect(Collectors.toList());
        mesDispatch.setOperator(JSON.toJSONString(operatorIds));
        mesDispatch.setDeviceId(JSON.toJSONString(deviceIds));
        mesDispatch.setOperatorDevicesBind(JSON.toJSONString(bindDOS));
        this.baseMapper.updateById(mesDispatch);
        return Response.succeed();
    }

    /**
     * 派工单开工后还能追加操作工
     * 华德对用户开工时机不做限制：用户可以随时给开工的派工单追加操作工
     * 单次开工只能追加一个操作工和多个设备，当次追加的操作工和设备自动绑定
     * @param mesDispatchPageParam
     * @return
     */
    @Override
    public Response<?> workStart(MesDispatchHangUpParam mesDispatchPageParam) {
        Integer dispatchId = mesDispatchPageParam.getDispatchId();
        MesDispatch mesDispatchDO = this.baseMapper.selectById(dispatchId);
        if(Const.PlanType.wan_gong.equals(mesDispatchDO.getDispatchStatus())){
            return Response.fail("该派工单已完工！");
        }
        //校验派工单状态
//        if (!Const.PlanType.gua_qi.equals(mesDispatchDO.getDispatchStatus()) && !Const.PlanType.dai_chan.equals(mesDispatchDO.getDispatchStatus())){
//            return Response.fail("该派工单不是待产或挂起状态，不能开工！");
//        }
        MesProcessPlanDetail planDetailDO = mesProcessPlanDetailService.getById(mesDispatchDO.getProcessPlanDetailId());
        //校验工序计划状态
        if (!Const.producePlanStatus.DISPATCHED.equals(planDetailDO.getDoDispatching())){
            return Response.fail("该工序计划下的派工单，不能开工！");
        }
        //这些代码考虑到产品可能用到，暂时不删
//        Integer serialNumber = planDetailDO.getSerialNumber();
//        if (!serialNumber.equals(1)){
//            List<MesProcessPlanDetail> planDetailDOS = mesProcessPlanDetailService.list(new LambdaQueryWrapper<MesProcessPlanDetail>()
//                    .eq(MesProcessPlanDetail::getProcessPlanId, planDetailDO.getProcessPlanId())
//                    .eq(MesProcessPlanDetail::getSerialNumber, serialNumber - 1));
//            List<MesDispatch> preSerialDispatchDOS = this.list(new LambdaQueryWrapper<MesDispatch>().eq(MesDispatch::getProcessPlanDetailId, planDetailDOS.get(0).getId()));
//            List<Integer> preSerialDispatchIds = preSerialDispatchDOS.stream().map(MesDispatch::getId).collect(Collectors.toList());
            //华德不做限制
//            int count = mesProcessReportService.count(new LambdaQueryWrapper<MesProcessReport>().in(MesProcessReport::getDispatchId, preSerialDispatchIds));
//            if (count <= 0){
//                return Response.fail("上一道工序未报工！");
//            }
//        }
        //校验设备是否空闲 华德是流水线，不校验设备状态
//        List<String> filterString = JSON.parseArray(mesDispatch.getDeviceId()).stream()
//                .map(item -> " JSON_CONTAINS( device_id, CAST( ' " + item.toString() + " ' AS JSON ), '$' )").collect(Collectors.toList());
//        List<MesDispatch> diviceErrors = this.list(new LambdaQueryWrapper<MesDispatch>().eq(MesDispatch::getDispatchStatus, Const.PlanType.kai_gong)
//                .last("and (" + StringUtils.join(filterString, " or ") + ")" ));
//        if (ObjectUtil.isNotEmpty(diviceErrors)){
//            return Response.fail("该派工单下存在已开工设备，不能开工！");
//
//        }
        //校验追加的操作工是否空闲


        List<Integer> addOperators = JSON.parseArray(mesDispatchPageParam.getReportUsers(), Integer.class);
        List<Integer> addDevices = JSON.parseArray(mesDispatchPageParam.getDeviceIds(), Integer.class);
        List<Integer> workingOperators = JSON.parseArray(mesDispatchDO.getOperator(), Integer.class);
        List<Integer> workingDevices = JSON.parseArray(mesDispatchDO.getDeviceId(), Integer.class);
        addOperators.removeAll(workingOperators);
        addDevices.removeAll(workingDevices);
        if (ObjectUtil.isEmpty(addOperators)){
            return Response.fail("未选择空闲操作工！");
        }
        if (addOperators.size() > 1){
            return Response.fail("每次只能开工一位操作工！");
        }
        List<MesDispatchHis> dispatchHisDOS = mesDispatchHisService.list(new LambdaQueryWrapper<MesDispatchHis>()
                .in(MesDispatchHis::getStartHangId, addOperators)
                .isNull(MesDispatchHis::getHangTime));
        List<MesDispatchHis> guaqiDOS = dispatchHisDOS.stream().filter(item -> item.getSign().equals(0)).collect(Collectors.toList());
//        List<MesDispatchHis> userErrors = dispatchHisDOS.stream().filter(item -> item.getSign().equals(1)).collect(Collectors.toList());
//        if (ObjectUtil.isNotEmpty(userErrors) && (ObjectUtil.isEmpty(mesDispatchPageParam.getForce()) || mesDispatchPageParam.getForce().equals(0))){
//            List<Integer> userErrorIds = userErrors.stream().map(MesDispatchHis::getStartHangId).collect(Collectors.toList());
//            List<SysUser> userErrorDOS = sysUserService.list(new LambdaQueryWrapper<SysUser>().in(SysUser::getId, userErrorIds));
//            return Response.fail(StringUtils.join(userErrorDOS.stream()
//                    .map(SysUser::getRealName).collect(Collectors.toList()), ",") + "存在开工状态派工单，不能开工！");
//        }

        //1、将挂起停止
        Date nowTime = new Date();
        if (ObjectUtil.isNotEmpty(guaqiDOS)){
            guaqiDOS.forEach(item -> {
                item.setHangTime(nowTime);
                item.setManHour(dateHour(item.getStartTime(), nowTime));
            });
        }

        //2、生成开工挂起记录  考虑到以后可能会统计每个操作工的工时，开工挂起记录精确到操作工
        List<MesDispatchHis> kaigongDTO = addOperators.stream().map(item -> {
            MesDispatchHis kaiGongHis = new MesDispatchHis(dispatchId, nowTime, item, 1, mesDispatchPageParam.getReason());
            kaiGongHis.setDeviceIds(JSON.toJSONString(addDevices));
            kaiGongHis.setCreateBy(TokenUtil.getUserId());
            return kaiGongHis;
        }).collect(Collectors.toList());
        guaqiDOS.addAll(kaigongDTO);
        mesDispatchHisService.saveOrUpdateBatch(guaqiDOS);

        //3、更新工单状态
        List<Integer> devices = JSON.parseArray(mesDispatchPageParam.getDeviceIds(), Integer.class);
        List<Integer> deviceBefore = JSON.parseArray(mesDispatchDO.getDeviceId(), Integer.class);
        deviceBefore.addAll(devices);
        deviceBefore = deviceBefore.stream().distinct().collect(Collectors.toList());
        workingOperators.addAll(addOperators);
        mesDispatchDO.setDispatchStatus(Const.PlanType.kai_gong);
        mesDispatchDO.setDeviceId(JSON.toJSONString(deviceBefore));//虽然已经有了operator_devices_bind字段，但是这个operator和device_id不好删除，因为已经和前端对接了
        mesDispatchDO.setOperator(JSON.toJSONString(workingOperators));//虽然已经有了operator_devices_bind字段，但是这个operator和device_id不好删除，因为已经和前端对接了
        List<MesOperatorDevicesBindDTO> bindDOS = JSON.parseArray(mesDispatchDO.getOperatorDevicesBind(), MesOperatorDevicesBindDTO.class);
        MesOperatorDevicesBindDTO bindDTO = new MesOperatorDevicesBindDTO();
        bindDTO.setDevices(addDevices);
        bindDTO.setOperator(addOperators.get(0)); //每次只能增加一个操作工
        bindDOS.add(bindDTO);
        mesDispatchDO.setOperatorDevicesBind(JSON.toJSONString(bindDOS));
        this.baseMapper.updateById(mesDispatchDO);
        if (ObjectUtil.isEmpty(planDetailDO.getRealityStartTime())){
            planDetailDO.setRealityStartTime(nowTime);
            mesProcessPlanDetailService.updateById(planDetailDO);
        }

        return Response.succeed();
    }

    /**
     * 新增文档记录
     * @param cusDocumentList
     * @param type
     * @param sourceId
     */
    public void saveDocumentList(List<SysFileHis> cusDocumentList, Integer type, Integer sourceId) {
        if (ObjectUtil.isEmpty(cusDocumentList)){
            return;
        }
        List<MesDocument> mesDocuments = cusDocumentList.stream().map(item -> {
            MesDocument mesDocument = new MesDocument();
            mesDocument.setMesId(sourceId);
            mesDocument.setMesType(type);
            mesDocument.setDocumentId(item.getId());
            return mesDocument;
        }).collect(Collectors.toList());
        if(ObjectUtil.isNotEmpty(mesDocuments)){
            mesDocumentService.saveBatch(mesDocuments);
        }
    }

    /**
     *
     * @param vo
     * @return
     */
    @Override
    public Pair<Integer, String> report(MesProcessReportAddVo vo) {
        List<MesProcessReport> reportVOS = vo.getReportList();
        List<Integer> reportUserVOS = reportVOS.stream().map(MesProcessReport::getReportUsers)
                .flatMap(item -> new ArrayList<>(JSON.parseArray(item)).stream().map(item2 -> (Integer) item2)).collect(Collectors.toList());
        BigDecimal completeNumNow = reportVOS.stream().map(MesProcessReport::getCompletionCount).filter(item -> ObjectUtil.isNotEmpty(item)).reduce(BigDecimal::add).orElse(BigDecimal.ZERO);
        BigDecimal conformityCountNow = reportVOS.stream().map(MesProcessReport::getConformityCount).filter(item -> ObjectUtil.isNotEmpty(item)).reduce(BigDecimal::add).orElse(BigDecimal.ZERO);
        BigDecimal reworkCountNow = reportVOS.stream().map(MesProcessReport::getReworkCount).filter(item -> ObjectUtil.isNotEmpty(item)).reduce(BigDecimal::add).orElse(BigDecimal.ZERO);
        BigDecimal scrapCountNow = reportVOS.stream().map(MesProcessReport::getScrapCount).filter(item -> ObjectUtil.isNotEmpty(item)).reduce(BigDecimal::add).orElse(BigDecimal.ZERO);

        Integer dispatchId = reportVOS.get(0).getDispatchId();
        MesDispatch mesDispatchDO = this.baseMapper.selectById(dispatchId);
        MesProcessPlanDetail planDetailDO = mesProcessPlanDetailService.getById(mesDispatchDO.getProcessPlanDetailId());
        List<MesProcessPlanDetail> planDetailDOS = mesProcessPlanDetailService.list(new LambdaQueryWrapper<MesProcessPlanDetail>()
                .eq(MesProcessPlanDetail::getProcessPlanId, planDetailDO.getProcessPlanId())
                .orderByAsc(MesProcessPlanDetail::getSerialNumber));
        MesProcessPlan processPlanDO = mesProcessPlanService.getById(planDetailDO.getProcessPlanId());
//        Map<Integer, MesProcessPlanDetail> planDetailDOSMap = planDetailDOS.stream().collect(Collectors.toMap(MesProcessPlanDetail::getId, Iterable -> Iterable, (v1, v2) -> v1));
        // 校验1 派工的状态校验
        if (!Const.PlanType.kai_gong.equals(mesDispatchDO.getDispatchStatus())){
            return Pair.of(2, "该派工单不是开工状态，不能报工！");
        }
        List<MesDispatch> dispatchOfPlanDetailDOS = this.baseMapper.selectList(new LambdaQueryWrapper<MesDispatch>()
                .eq(MesDispatch::getProcessPlanDetailId, planDetailDO.getId()));
        Map<Integer, MesDispatch> dispatchOfPlanDetailMap = dispatchOfPlanDetailDOS.stream().collect(Collectors.toMap(item -> item.getId(), Iterable -> Iterable, (v1, v2) -> v1));
        //这道工序的历史完工数量
        BigDecimal completeNumHistory = dispatchOfPlanDetailDOS.stream().map(MesDispatch::getCompleteNum).reduce(BigDecimal::add).orElse(BigDecimal.ZERO);
        BigDecimal completionCountNow = completeNumNow.add(completeNumHistory); //该道工序目前的完工数量：历史完工+现在完工
        List<MesDispatch> previousDispatcheDOS = new ArrayList<>(); // 上道工序的所有派工单
        BigDecimal previousConformityNum = BigDecimal.ZERO;  //上道工序的合格数量
        MesProcessPlanDetail previousPlanDetailDO = new MesProcessPlanDetail(); //上一道工序
        //校验2 该道工序的报工数量不超过前一道工序的合格数量
        boolean firstSerial = planDetailDO.getSerialNumber().equals(1);
        if (!firstSerial){
            previousPlanDetailDO = planDetailDOS.get(planDetailDO.getSerialNumber() - 2);
            previousDispatcheDOS = this.baseMapper.selectList(new LambdaQueryWrapper<MesDispatch>().eq(MesDispatch::getProcessPlanDetailId, previousPlanDetailDO.getId()));
            previousConformityNum = previousDispatcheDOS.stream().map(md -> Objects.isNull(md.getConformityNum()) ? BigDecimal.ZERO : md.getConformityNum()).reduce(BigDecimal.ZERO, BigDecimal::add);
            if (completionCountNow.compareTo(previousConformityNum) > 0){
                return Pair.of(2, "完工数量超过上一道工序的合格数量");
            }
        }
        ////////////////////////////////////////////================以上部分是校验===============////////////////////////////////////////////
        //操作1、保存报工单
        QueryWrapper queryWrapper = new QueryWrapper();
        queryWrapper.eq("document_id",Const.DOCUMENT_ID.MES_DISPATCH);
        SysDocumentNum sysDocumentNum = sysDocumentNumService.getOne(queryWrapper);
        List<SysFileHis> sysFileHisList = vo.getSysFileHisList();
        sysFileHisService.saveBatch(sysFileHisList);

//        mesProcessReport.setReportUsers(mesDispatchDO.getOperator());
//        mesProcessReport.setDeviceIds(mesDispatchDO.getDeviceId());
//        mesProcessReport.setProcessReportCode(sysDocumentNumService.generatorDispatchCodeCode(sysDocumentNum));
        List<MesOperatorDevicesBindDTO> bindDOS = JSON.parseArray(mesDispatchDO.getOperatorDevicesBind(), MesOperatorDevicesBindDTO.class);
        Map<Integer, MesOperatorDevicesBindDTO> bindDOSMap = bindDOS.stream().collect(Collectors.toMap(item -> item.getOperator(), item -> item, (v1, v2) -> v1));
        reportVOS.forEach(item ->{
            List<Integer> operator = JSON.parseArray(item.getReportUsers(), Integer.class);
            item.setProcessReportCode(sysDocumentNumService.generatorDispatchCodeCode(sysDocumentNum));
            item.setDeviceIds(JSON.toJSONString(bindDOSMap.get(operator.get(0))));
        });
        mesProcessReportService.saveBatch(reportVOS);
        reportVOS.forEach(item ->{
            this.saveDocumentList(sysFileHisList, Const.FileType.MES_REPORT, item.getId());
        });


        //todo 新增字段替换计划数量
        //该道工序能否完工：1、该道工序是第一道工序，并且完工数量达到计划数量  ||  2、该道工序非第一道工序、上道工序完工、该道工序完工数量达到上道工序合格数量
        BigDecimal finishCount = BigDecimal.ZERO;
        finishCount = planDetailDO.getPlanCount();
        if (ObjectUtil.isNotEmpty(processPlanDO.getFeedingNum()) && processPlanDO.getFeedingNum().compareTo(BigDecimal.ZERO) > 0)
            finishCount = processPlanDO.getFeedingNum();
        boolean finished = (firstSerial && (completionCountNow.compareTo(finishCount) >= 0))
                || ( !firstSerial && Const.producePlanStatus.COMPLETE_WORK.equals(previousPlanDetailDO.getDoDispatching()) && (completionCountNow.compareTo(previousConformityNum) >= 0) );
        //操作2：该道工序完工： 该道工序明细完工、该道工序明细下所有派工单完工
        Date nowDate = new Date();
        ArrayList<Integer> dispatchIds = new ArrayList<>(); //要完工的派工的
        MesDispatch mesDispatchDO2 = dispatchOfPlanDetailMap.get(mesDispatchDO.getId());//和mesDispatchDO本质是一条记录，是为了使用this.saveOrUpdateBatch()批处理
        mesDispatchDO2.setCompleteNum((ObjectUtil.isEmpty(mesDispatchDO2.getCompleteNum()) ? BigDecimal.ZERO : mesDispatchDO2.getCompleteNum())
                .add(completeNumNow));
        mesDispatchDO2.setConformityNum((ObjectUtil.isEmpty(mesDispatchDO2.getConformityNum()) ? BigDecimal.ZERO : mesDispatchDO2.getConformityNum())
                .add(conformityCountNow));
        mesDispatchDO2.setScrapCount((ObjectUtil.isEmpty(mesDispatchDO2.getScrapCount()) ? BigDecimal.ZERO : mesDispatchDO2.getScrapCount())
                .add(scrapCountNow));
        planDetailDO.setCompletionCount((ObjectUtil.isNotEmpty(planDetailDO.getCompletionCount()) ? planDetailDO.getCompletionCount() : BigDecimal.ZERO)
                .add(completeNumNow));
        planDetailDO.setConformityCount((ObjectUtil.isNotEmpty(planDetailDO.getConformityCount()) ? planDetailDO.getConformityCount() : BigDecimal.ZERO)
                .add(conformityCountNow));
        planDetailDO.setScrapCount((ObjectUtil.isNotEmpty(planDetailDO.getScrapCount()) ? planDetailDO.getScrapCount() : BigDecimal.ZERO)
                .add(scrapCountNow));
        planDetailDO.setReworkCount((ObjectUtil.isNotEmpty(planDetailDO.getReworkCount()) ? planDetailDO.getReworkCount() : BigDecimal.ZERO)
                .add(reworkCountNow));
        planDetailDO.setDatacode(vo.getDatacode());   // 打印或包装工序记录此值用于条码打印

        List<Integer> workingOperators = JSON.parseArray(mesDispatchDO2.getOperator(), Integer.class);
        workingOperators.removeAll(reportUserVOS);
        if(finished){
            dispatchOfPlanDetailDOS.forEach(item ->{
                item.setActualEndTime(nowDate);
                item.setDispatchStatus(Const.PlanType.wan_gong);
                dispatchIds.add(item.getId());
            });
            planDetailDO.setDoDispatching(Const.producePlanStatus.COMPLETE_WORK);
            planDetailDO.setRealityEndTime(nowDate);
        }else {
            //操作3：该道工序未完工、没有开工状态的操作工： 该派工单状态改为待产

            dispatchIds.add(mesDispatchDO.getId());
            if (workingOperators.size() <= 0) mesDispatchDO2.setDispatchStatus(Const.PlanType.dai_chan);  //每次报工结束，将派工的状态改为待产状态
//            mesDispatchDO2.setOperator(JSON.toJSONString(workingOperators));  //每次报工结束，将派工的状态改为待产状态
//            this.baseMapper.updateById(mesDispatchDO2);
        }
        mesProcessPlanDetailService.updateById(planDetailDO);
        //操作4：派工单下的所有开工记录停止
        LambdaQueryWrapper<MesDispatchHis> dispatchHisWrapper = new LambdaQueryWrapper<MesDispatchHis>()
                .in(MesDispatchHis::getDispatchId, dispatchIds).eq(MesDispatchHis::getSign, 1).isNull(MesDispatchHis::getHangTime);
        if (!finished) dispatchHisWrapper.in(MesDispatchHis::getStartHangId, reportUserVOS);
        List<MesDispatchHis> kaiGongList = mesDispatchHisService.list(new LambdaQueryWrapper<MesDispatchHis>()
                .in(MesDispatchHis::getDispatchId, dispatchIds).eq(MesDispatchHis::getSign, 1).isNull(MesDispatchHis::getHangTime));
        Date nowTime = new Date();
        if (ObjectUtil.isNotEmpty(kaiGongList)){
            kaiGongList.forEach(item -> {
                item.setHangTime(nowTime);
                item.setManHour(dateHour(item.getStartTime(), nowTime));
                bindDOS.remove(bindDOSMap.get(item));
                bindDOSMap.remove(item);
            });
        }
        List<Integer> deviceIds = bindDOS.stream().flatMap(item -> item.getDevices().stream()).collect(Collectors.toList());
        List<Integer> operatorIds = bindDOS.stream().map(item -> item.getOperator()).collect(Collectors.toList());
        mesDispatchDO2.setOperatorDevicesBind(JSON.toJSONString(bindDOS));
        mesDispatchDO2.setOperator(JSON.toJSONString(operatorIds));
        mesDispatchDO2.setDeviceId(JSON.toJSONString(deviceIds));
        this.saveOrUpdateBatch(dispatchOfPlanDetailDOS);
        mesDispatchHisService.saveOrUpdateBatch(kaiGongList);
        //操作5：若工序计划明细刚好完工，则校验本道工序是否是最后一道工序 ==> 工序计划主表状态改为完工、生产计划完工
        if (finished && planDetailDO.getSerialNumber().equals(planDetailDOS.size())){
            processPlanDO.setProcessStatus(Const.producePlanStatus.COMPLETE_WORK);
            mesProcessPlanService.updateById(processPlanDO);
            MesProducePlan mesProducePlan = new MesProducePlan();
            mesProducePlan.setId(processPlanDO.getProducePlanId());
            mesProducePlan.setPlanStatus(Const.producePlanStatus.COMPLETE_WORK);
            mesProducePlanService.updateById(mesProducePlan);
        }
        //操作6 产品检验工序完工后推送检验单
        if (finished && planDetailDO.getProcessType().equals(10821)){
            return Pair.of(1, planDetailDO.getId().toString());
        }
        return Pair.of(0, null);
    }

    @Override
    public Response<?> getReportNum(MesProcessCardVo vo) {
        HashMap<String, Object> param = new HashMap<>();
        param.put("dispatchId",vo.getDispatchId());
        MesProcessPlanDetail lastProcessDetailPlan = mesProcessPlanDetailService.getDetailBySerial(param);//上道工序
        if (ObjectUtil.isEmpty(lastProcessDetailPlan)){
            return Response.fail("未查询到上一道工序记录");
        }
        List<MesDispatch> dispatchDOS = this.baseMapper.selectList(new LambdaQueryWrapper<MesDispatch>()
                .eq(MesDispatch::getProcessPlanDetailId, lastProcessDetailPlan.getId()));
        List<Integer> dispatchIds = dispatchDOS.stream().map(MesDispatch::getId).collect(Collectors.toList());
        List<MesProcessReport> reportDOS = mesProcessReportService.list(new LambdaQueryWrapper<MesProcessReport>()
                .in(MesProcessReport::getDispatchId, dispatchIds).eq(MesProcessReport::getCardCode, vo.getCode()));
        HashMap<String, Object> result = new HashMap<>();
        BigDecimal completionCount = reportDOS.stream().filter(item -> ObjectUtil.isNotEmpty(item.getCompletionCount())).map(MesProcessReport::getCompletionCount).reduce(BigDecimal::add).orElse(BigDecimal.ZERO);
        BigDecimal conformityCount = reportDOS.stream().filter(item -> ObjectUtil.isNotEmpty(item.getConformityCount())).map(MesProcessReport::getConformityCount).reduce(BigDecimal::add).orElse(BigDecimal.ZERO);
        BigDecimal scrapCount = reportDOS.stream().filter(item -> ObjectUtil.isNotEmpty(item.getScrapCount())).map(MesProcessReport::getScrapCount).reduce(BigDecimal::add).orElse(BigDecimal.ZERO);
        BigDecimal reworkCount = reportDOS.stream().filter(item -> ObjectUtil.isNotEmpty(item.getReworkCount())).map(MesProcessReport::getReworkCount).reduce(BigDecimal::add).orElse(BigDecimal.ZERO);
        result.put("completionCount",completionCount);//完工数量
        result.put("conformityCount",conformityCount);//合格数量
        result.put("scrapCount",scrapCount);//报废数量
        result.put("reworkCount",reworkCount);//返工数量
        return Response.succeed(result);
    }


    // 两个日期相减得到的小时
    public static BigDecimal dateHour(Date beginDate, Date endDate) {
        long beginMs = beginDate.getTime();
        long endMs = endDate.getTime();
        BigDecimal subtractMs = new BigDecimal(endMs - beginMs);
        BigDecimal msss = BigDecimal.valueOf((int)(1000 * 60 * 60));
        BigDecimal result = subtractMs.divide(msss,3,BigDecimal.ROUND_HALF_UP);
        return result;
    }
}




